#ifndef DW_MATHS_QUATERNION_H
#	define DW_MATHS_QUATERNION_H

#include "maths/matrix.h"
#include "maths/vector.h"

namespace dw {
	namespace maths {

template< typename T >
struct Quaternion;

template< typename T >
inline Quaternion< T >							operator-(const Quaternion< T >& rhs);

template< typename T >
inline Quaternion< T >							operator~(const Quaternion< T >& rhs);

template< typename T >
inline Quaternion< T >							operator+(const Quaternion< T >& lhs, const Quaternion< T >& rhs);

template< typename T >
inline Quaternion< T >							operator-(const Quaternion< T >& lhs, const Quaternion< T >& rhs);

template< typename T >
inline Quaternion< T >							operator*(const Quaternion< T >& lhs, const Quaternion< T >& rhs);

template< typename T >
inline Quaternion< T >							operator*(const Quaternion< T >& lhs, const T& rhs);

template< typename T >
inline Quaternion< T >							operator*(const T& lhs, const Quaternion< T >& rhs);

template< typename T >
inline Vector< T, 3 >								operator*(const Quaternion< T >& lhs, const Vector< T, 3 >& rhs);

template< typename T >
struct Quaternion
{

// Fields
public:

	union
	{
		struct  
		{
			T s;
			Vector< T, 3 >	v;
		};
		struct 
		{
			T w;
			T x;
			T y;
			T z;
		};
	};

	static const Quaternion< T >			zero;

	static const Quaternion< T >			identity;

// Constructors
public:

	inline Quaternion();

	inline 													Quaternion(const T& s, const Vector< T, 3 >& v);

	inline 													Quaternion(const T& w, const T& x, const T& y, const T& z);

	inline 													Quaternion(const T& yaw, const T& pitch, const T& roll);
	
	inline 													Quaternion(const Vector< T, 3 >& axis, const T& angle);

	inline 													Quaternion(const Vector< T, 3 >& dir, const Vector< T, 3 >& up);
	
	inline 													Quaternion(const Matrix< T, 3, 3 >& m);

// Methods
public:

	inline Quaternion< T >& 					operator+=(const Quaternion< T >& q);

	inline Quaternion< T >& 					operator-=(const Quaternion< T >& q);

	inline Quaternion< T >& 					operator*=(const Quaternion< T >& q);

	inline Quaternion< T >& 					operator*=(const T& s);

	inline Quaternion< T >& 					operator/=(const T& s);



	inline Vector< T, 3 >							getAxis()		const ;

	inline T getAngle()	const ;

	inline T getYaw()		const ;

	inline T getPitch()	const ;

	inline T getRoll()		const ;



	inline T norm() const ;

	inline Quaternion< T >&						normalize();



	inline Matrix< T, 3, 3 >					toMatrix() const ;

	inline std::string toString() const ;



	static Quaternion< T >						fromAxisAngle(const Vector< T, 3 >& axis, const T& angle);

	static Quaternion< T >						fromMatrix(const Matrix< T, 3, 3 >& m);

	static Quaternion< T >						fromEuler(const T& yaw, const T& pitch, const T& roll);

	static Quaternion< T >						fromSpherical(const T& longitude, const T& latitude, const T& angle);




	static T dot(const Quaternion< T >& q0, const Quaternion< T >& q1);

	static Quaternion< T >						conjugate(const Quaternion< T >& q);

	static Quaternion< T >						inverse(const Quaternion< T >& q);

	static Quaternion< T >						unit(const Quaternion< T >& q);

	static Quaternion< T >						rotation(const Vector< T, 3 >& axis, const T& angle);

	static Quaternion< T >						rotation(const T& yaw, const T& pitch, const T& roll);

	static Quaternion< T >						rotation(const Matrix< T, 3, 3 >& m);

	static Quaternion< T >						rotation(const Vector< T, 3 >& from, const Vector< T, 3 >& to);

	static Quaternion< T >						lerp(const Quaternion< T >& q0, const Quaternion< T >& q1, const T& t);

	static Quaternion< T >						slerp(const Quaternion< T >& q0, const Quaternion< T >& q1, const T& t);

	static Quaternion< T >						squad(const Quaternion< T >& q0, const Quaternion< T >& q1, const Quaternion< T >& q2, const Quaternion< T >& q3, const T& t);



	friend Quaternion< T >						operator+<>(const Quaternion< T >& lhs, const Quaternion< T >& rhs);

	friend Quaternion< T >						operator-<>(const Quaternion< T >& lhs, const Quaternion< T >& rhs);

	friend Quaternion< T >						operator-<>(const Quaternion< T >& rhs);

	friend Quaternion< T >						operator*<>(const Quaternion< T >& lhs, const Quaternion< T >& rhs);

	friend Quaternion< T >						operator*<>(const Quaternion< T >& lhs, const T& rhs);

	friend Quaternion< T >						operator*<>(const T& lhs, const Quaternion< T >& rhs);

	friend Vector< T, 3 >							operator*<>(const Quaternion< T >& lhs, const Vector< T, 3 >& rhs);

};

typedef Quaternion< float >					Quaternionf;

typedef Quaternion< double >				Quaterniond;

	} // namespace maths
} // namespace dw

#include "maths/quaternion.inl"

#endif // !DW_MATHS_QUATERNION_H
